use std::fmt::Display;

#[derive(Debug, PartialEq, Clone)]
pub enum TokenKind {
  // Literals
  Number(f64),

  // Operators
  Plus,              // +
  Minus,             // -
  Asterisk,          // *
  Slash,             // /
  Percent,           // %
  Caret,             // ^
  DoubleAsterisk,    // **
  Assign,            // =
  EqualsEquals,      // ==
  Bang,              // !
  BangEquals,        // !=
  GreaterThan,       // >
  LessThan,          // <
  GreaterThanEquals, // >=
  LessThanEquals,    // <=

  // Separators
  LeftParen,    // (
  RightParen,   // )
  OpenBrace,    // {
  CloseBrace,   // }
  OpenBracket,  // [
  CloseBracket, // ]
  Comma,        // ,
  Colon,        // :
  SemiColon,    // ;

  StringLiteral,    // 字符串
  DoubleQuoteStart, // 双引号 "
  DoubleQuoteEnd,   // 双引号 "

  // Keywords
  Let,
  True,
  False,

  // Other
  Identifier,
  Whitespace,
  Eof,
  Bad,
}

impl Display for TokenKind {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      Self::Number(_) => write!(f, "Number"),
      Self::Plus => write!(f, "+"),
      Self::Minus => write!(f, "-"),
      Self::Asterisk => write!(f, "*"),
      Self::Slash => write!(f, "/"),
      Self::Percent => write!(f, "%"),
      Self::Caret => write!(f, "^"),
      Self::DoubleAsterisk => write!(f, "**"),
      Self::Assign => write!(f, "="),
      Self::EqualsEquals => write!(f, "=="),
      Self::Bang => write!(f, "!"),
      Self::BangEquals => write!(f, "!="),
      Self::GreaterThan => write!(f, ">"),
      Self::LessThan => write!(f, "<"),
      Self::GreaterThanEquals => write!(f, ">="),
      Self::LessThanEquals => write!(f, "<="),
      Self::LeftParen => write!(f, "("),
      Self::RightParen => write!(f, ")"),
      Self::OpenBrace => write!(f, "{{"),
      Self::CloseBrace => write!(f, "}}"),
      Self::OpenBracket => write!(f, "["),
      Self::CloseBracket => write!(f, "]"),
      Self::Comma => write!(f, ","),
      Self::Colon => write!(f, ":"),
      Self::SemiColon => write!(f, ";"),
      Self::StringLiteral => write!(f, "String"),
      Self::DoubleQuoteStart | Self::DoubleQuoteEnd => write!(f, "\""),
      Self::Let => write!(f, "Let"),
      Self::True => write!(f, "True"),
      Self::False => write!(f, "False"),
      Self::Identifier => write!(f, "Identifier"),
      Self::Whitespace => write!(f, "Whitespace"),
      Self::Eof => write!(f, "Eof"),
      Self::Bad => write!(f, "Bad"),
    }
  }
}

#[derive(Debug, Clone)]
pub struct TextSpan {
  pub start: usize,
  pub end: usize,
  pub literal: String,
}

impl TextSpan {
  pub fn new(start: usize, end: usize, literal: String) -> Self {
    Self {
      start,
      end,
      literal,
    }
  }

  pub fn length(&self) -> usize {
    self.end - self.start
  }
}

#[derive(Debug, Clone)]
pub struct Token {
  pub kind: TokenKind,
  pub span: TextSpan,
}

impl Token {
  pub fn new(kind: TokenKind, span: TextSpan) -> Self {
    Self { kind, span }
  }
}
